
/* <<< COPYRIGHT START >>>
 * Copyright 2006-Present OxygenSoftwareLibrary.com
 * Licensed under the GNU Lesser General Public License.
 * http://www.gnu.org/licenses/lgpl.html
 * 
 * @author: Ugorji Dick-Nwoke
 * <<< COPYRIGHT END >>>
 */


package oxygen.tool.wlfacade.helpers;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import javax.management.MBeanAttributeInfo;
import javax.management.MBeanInfo;
import javax.management.ObjectName;

import oxygen.tool.facade.FacadeBenignException;
import oxygen.tool.wlfacade.WLFacadeContextHelper;
import oxygen.tool.wlfacade.WLJMXHandler;
import oxygen.tool.wlfacade.WLMBSTree;
import oxygen.tool.wlfacade.WLScriptHelper;
import weblogic.management.WebLogicMBean;
import weblogic.management.WebLogicObjectName;

/*
SOME MISC NOTES for config2sc
- It should be a no-brainer to convert a config.xml to a script
- Thus, the script should take the same strings that the config.xml
  takes
- Consequently, have the following methods
  setatt(String key, Object value)
  setattstr(String key, String value)
  get(String key)
  getattstr(String key)

- Note that some attribute values take MBeans also ... e.g. Targets,
  ConnectionPool, Store, etc. And sometimes, the objects are looked
  for under different areas e.g. Store is JDBCStore or FileStore,
  Targets is array of cluster or server, etc
  To handle this, keep a map of (sort of synonyms)
  Attribute Name --> List of Possible Types
  Store                    FileStore, JDBCStore
  Targets                  Server, Cluster
  ConnectionPool           JDBCConnectionPool
  Template                 JMSTemplate
  ...

  When the attribute is about to be set, we find the attribute type
  under the MBean tree, and get the signature, and if it takes an
  array, we tokenize it into an array on , or ;. If it takes a
  ConfigurationMBean, we look for the MBean in the MBean Home. If it
  takes any other Object, we make the string into the object, and pass
  it on.

the only trees you can browse are the
- config tree
- some runtime tree ??
However, you can switch to a runtime mbean, and invoke, set or do get operations on it
you can set admin mode, but no browsing there

# just before you do admin functions, do a 
wl.admin()
wl.setattstr(...)
wl.config()
// continue your browsing



// NOT YET BAKED ... 

*/
// these actions (except invoke) can only be done on the AS
public class WLConfig2 {
  private static Map mbeansynonyms = new HashMap();
  private static boolean DEBUG = false;
  static {
    //add all synonyms here
    mbeansynonyms.put("Store", new String[]{"JMSFileStore", "JMSJDBCStore"});
    mbeansynonyms.put("Targets", new String[]{"Server", "Cluster"});
    mbeansynonyms.put("EnabledServers", new String[]{"Server"});
    mbeansynonyms.put("ConnectionPool", new String[]{"JDBCConnectionPool"});
    //mbeansynonyms.put("Template", new String[]{"JMSTemplate"});
    mbeansynonyms.put("TemporaryTemplate", new String[]{"JMSTemplate"});
    mbeansynonyms.put("TargetedTrapDestinations", new String[]{"SNMPTrapDestination"});
  }
  
  protected static String getattstr(WLFacadeContextHelper wlctx, String attname, Object obj) 
    throws Exception {
    if(obj == null) {
      return null;
    }
    String type = getAttType(wlctx, attname);
    
    if(isArrayType(type)) { 
      String elemtype = type.substring(2, type.length()-1);
      Object[] objs = (Object[])obj;
      StringBuffer buf = new StringBuffer();
      for(int i = 0; i < objs.length; i++) {
        Object obj0 = objs[i];
        buf.append(getSingleAttValAsStr(elemtype, obj0)).append(",");
      }
      return buf.toString();
    } else if(isCollectionType(type)) {
      //Collection objs = (Collection)obj;
      //StringBuffer buf = new StringBuffer();
      //for(Iterator itr = objs.iterator(); itr.hasNext(); ) {
      //  Object obj0 = itr.next();
      //  buf.append(getSingleAttValAsStr(obj0)).append(",");
      //}
      //return buf.toString();
      throw new FacadeBenignException("There is no way to handle getting Collection value as a string");
      //     } else if(isPrimitiveType(type)) {
      //       return getSingleAttValAsStr(type, obj);
      //     } else if(isWeblogicMBeanType(type)) {
      //       return getSingleAttValAsStr(type, obj);
    } else {
      return getSingleAttValAsStr(type, obj);
    }
      
  }
  
  protected static Object setattstr(WLFacadeContextHelper wlctx, String attname, String attval) 
    throws Exception {
    WLMBSTree wltree = (WLMBSTree)wlctx.getCurrentTree();
    p1("setattstr: cmo: " + wltree.pointer() + " attname: " + attname + " attval: " + attval);
    Object attvalobj = null;
    String type = getAttType(wlctx, attname);
    
    if(isArrayType(type)) {
      String elemtype = type.substring(2, type.length()-1);
      Class elemclass = Class.forName(elemtype);
      boolean isWlElemtype = isWeblogicMBeanType(elemtype);
      StringTokenizer stz = new StringTokenizer(attval, ",;");
      List l = new ArrayList();
      while(stz.hasMoreTokens()) {
        String tok = stz.nextToken();
        Object obj = tok;
        //if(WebLogicMBean.class.isAssignableFrom(elemclass)) {
        obj = getSingleAttValAsObj(wlctx, attname, elemtype, tok);
        l.add(obj);
      }
      Object[] oa = null;
      if(isWlElemtype) {
        oa = new WebLogicObjectName[l.size()];
      } else {
        oa = (Object[])java.lang.reflect.Array.newInstance(elemclass, l.size());
      }
      p1("elemclass: " + elemclass);
      p1("oa.getComponentType(): " + oa.getClass().getComponentType());
      p1("Setting Att Val to: " + l);
      l.toArray(oa);
      attvalobj = oa;
    } else if(isCollectionType(type)) {
      throw new FacadeBenignException("There is no way to handle setting Collection value as a string");
      //     } else if(isPrimitiveType(type)) {
      //       attvalobj = getSingleAttValAsObj(wlctx, attname, type, attval);
      //     } else if(isWeblogicMBeanType(type)) {      
      //       attvalobj = getSingleAttValAsObj(wlctx, attname, type, attval);
    } else {
      attvalobj = getSingleAttValAsObj(wlctx, attname, type, attval);      
    }
    return attvalobj;
  }
  
  private static String getAttType(WLFacadeContextHelper wlctx, String attname) throws Exception {
    WLMBSTree wltree = (WLMBSTree)wlctx.getCurrentTree();
    Object mbs = wltree.getMBS();
    ObjectName cmo = (ObjectName)wltree.pointer();
    MBeanInfo mbinfo = WLScriptHelper.mbs_getMBeanInfo(mbs, cmo);
    MBeanAttributeInfo[] attsinfo = mbinfo.getAttributes(); 
    for(int i = 0; i < attsinfo.length; i++) {
      String attname0 = attsinfo[i].getName();
      if(attname0.equals(attname)) {
        return attsinfo[i].getType();
      }
    }
    throw new FacadeBenignException("This attribute does not exist for this ObjectName");
  }
  
  private static String getSingleAttValAsStr(String type, Object obj0) throws Exception {
    if(obj0 == null) {
      return ("");
    } else if(isWeblogicMBeanType(type)) { //obj0 instanceof WebLogicMBean) {
      if(obj0 instanceof WebLogicMBean) {
        return (((WebLogicMBean)obj0).getName());
      } else if(obj0 instanceof ObjectName) {
        return (((ObjectName)obj0).getKeyProperty("Name"));
      } else {
        throw new FacadeBenignException("Unable to get the string value for this object, where it was determined to be a WebLogicMBeanType");
      }
    } else {
      return (obj0.toString());
    }
  }
  
  private static Object getSingleAttValAsObj(WLFacadeContextHelper wlctx, String attname, String type, String attval) throws Exception {    
    WLMBSTree wltree = (WLMBSTree)wlctx.getCurrentTree();
    WLJMXHandler dmhdlr = wltree.getJMXHandler();
    if(attval == null) {
      return null;
    } else if(isPrimitiveType(type)) {
      if("int".equals(type)) {
        return new Integer(attval);
      } else if("double".equals(type)) {
        return new Double(attval);
      } else if("boolean".equals(type)) {
        return new Boolean(attval);
      } else {
        throw new FacadeBenignException("Not supported ... the type: " + type);
      }
    } else if(isWeblogicMBeanType(type)) {
      //check the map
      String[] synonyms = (String[])mbeansynonyms.get(attname);
      if(synonyms == null) {
        synonyms = new String[]{attname};
      }
      //p1("type='" + type + "' synonmyms='" + Arrays.asList(synonyms) + "'");
      Object obj = null;
      for(int i = 0; i < synonyms.length; i++) {
        String stringrep = dmhdlr.getStringRepGivenTypeName(new WLJMXHandler.TypeName(synonyms[i], attval, null));
        try {
          obj = null;
          String domain = wltree.getDomain();
          ObjectName pattern = dmhdlr.getLookupPattern(domain, stringrep);
          p1("pattern: " + pattern);
          ObjectName cmo = WLScriptHelper.getSingleObjName
            (wlctx.getConnection().getChangeMBS(), domain, stringrep, dmhdlr, null, false, null);
          //if(cmo != null) {
          //  obj = WLScriptHelper.tombean(wlctx, cmo);
          //}
          obj = cmo;
        } catch(Exception exc) {
          System.err.println("Error looking up Object for typename: " + stringrep + ": " + exc);
          //exc.printStackTrace();
        }
        if(obj != null) {
          obj = new WebLogicObjectName(obj.toString());
          break;
        }
      }
      p1("Returning WebLogicObject: " + obj + " of class: " + obj.getClass().getName());
      return obj;
    } else {
      return String.valueOf(attval);
    }
    //return null;
  }
  
  private static boolean isCollectionType(String type) {
    try {
      Class c = Class.forName(type);
      if(Collection.class.isAssignableFrom(c)) {
        return true;
      }
    } catch(Exception ignore) { }
    return false;
  }

  private static boolean isPrimitiveType(String type) {
    String[] primitives = new String[]{"int", "long", 
                                       "double", "float",
                                       "boolean"};
    List l = Arrays.asList(primitives);
    return l.contains(type);
  }
  
  private static boolean isArrayType(String type) {
    return type.startsWith("[L");
  }

  private static boolean isWeblogicMBeanType(String type) {
    try {
      Class c = Class.forName(type);
      if(WebLogicMBean.class.isAssignableFrom(c)) {
        return true;
      }
      if(WebLogicObjectName.class.isAssignableFrom(c)) {
        return true;
      }
    } catch(Exception ignore) { }
    return false;
  }
  
  private static void p1(Object s) {
    if(DEBUG) {
      if(s == null) {
        System.out.println(s);
      } else if(s instanceof Throwable) {
        ((Throwable)s).printStackTrace();
      } else {
        System.out.println(s);
      }
    }
  }
  
}

